package com.hzgj.bcl.soa.command;

import com.alibaba.fastjson.JSON;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.hzgj.bcl.redis.RedisClient;
import com.hzgj.bcl.soa.util.Pair;
import com.hzgj.bcl.stats.RedisKey;
import com.hzgj.bcl.util.IocContext;
import com.hzgj.bcl.util.lang.StrKit;

import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * Created by Administrator on 2016/9/2.
 */
public class InvokeServiceCommand extends CommandsInitializer.InternalCommand {
    private RedisClient redisClient;
    public static final String invoke_service_cols = "invokeServiceCols";

    public Cache<String, Map<String, List<String>>> cache = CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.MINUTES).build();

    @Override
    public String invoke(CommandOptions commandOptions) {
        return JSON.toJSONString(invoke0(commandOptions));
    }

    @Override
    public String getName() {
        return "invoke_service";
    }

    @Override
    public void start() {
        redisClient = IocContext.applicationContext.getBean(RedisClient.class);
    }

    @Override
    public Object invoke0(CommandOptions commandOptions) {
        String[] node = commandOptions.params.get("node");
        Map<String, Object> result = Maps.newHashMap();

        Object ret = serviceAndMethod(commandOptions);
        String[] firstService = commandOptions.params.get("firstService");
        if (firstService != null && ret != null) {
            Pair<List<String>, Object> pair = (Pair<List<String>, Object>) ret;
            result.put("nodes", serviceNodes(pair.getLeft().get(0)));
            result.put("services", pair);
        } else if (node != null && node[0].equals("1") && ret != null) {
            result.put("nodes", serviceNodes(commandOptions.params.get("service")[0]));
            result.put("services", ret);
        }
        return result;
    }

    private Object serviceNodes(String service) {
        String key = service + "_" + RedisKey.NODE;
        Set<String> nodes = redisClient.smembers(key);
        List<String> removing = Lists.newArrayList();
        Set<String> result = Sets.newHashSet();
        for (String node : nodes) {
            String time = redisClient.get(node + "_" + RedisKey.UPDATE_TIME);
            if (StrKit.notBlank(time)) {
                if (isNumeric(time)) {
                    if (Long.parseLong(time) < getBefore8Days()) {
                        removing.add(node);
                    }
                }
                result.add(node);
            } else {
                removing.add(node);
            }
        }
        if (removing.size() > 0) {
            redisClient.srem(key, removing.toArray(new String[0]));
        }
        return result;
    }

    private Object serviceAndMethod(CommandOptions commandOptions) {
        String[] service_param = commandOptions.params.get("service");
        String service = null;
        if (service_param != null) {
            service = service_param[0];
        }
        try {
            Map<String, List<String>> services = cache.get(invoke_service_cols, () -> {
                Set<String> keys = redisClient.smembers(RedisKey.SERVICE_METHOD_KEY);
                Map<String, List<String>> result = Maps.newHashMap();
                if (keys.size() > 0) {
                    keys.forEach(s -> {
                        long count = redisClient.zcount(RedisKey.getTotalCount(s), getBefore8Days(), System.currentTimeMillis());
                        if (count > 0) {
                            String[] cam = s.split("_");
                            if (cam != null && cam.length == 2) {
                                List<String> methods = result.get(cam[0]);
                                if (methods == null) {
                                    methods = Lists.newArrayList();
                                }
                                methods.add(cam[1]);
                                result.put(cam[0], methods);
                            }
                        }
                    });
                }
                return result;
            });

            if (services.size() > 0) {
                String[] firstService = commandOptions.params.get("firstService");
                if (firstService != null) {
                    List<String> ss = Lists.newArrayList(services.keySet());
                    return new Pair<>(ss, services.get(ss.get(0)));
                } else {
                    if (StrKit.isBlank(service)) {
                        return services.keySet();
                    } else {
                        return services.get(service);
                    }
                }
            }
        } catch (ExecutionException e) {
            logger.error("{}", e);
        }
        return null;
    }

    long getBefore8Days() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_YEAR, -8);
        return calendar.getTimeInMillis();
    }

    public static boolean isNumeric(String str) {
        if(str == null) {
            return false;
        } else {
            int sz = str.length();

            for(int i = 0; i < sz; ++i) {
                if(!Character.isDigit(str.charAt(i))) {
                    return false;
                }
            }

            return true;
        }
    }
}
